@page "/CascadingValuesParametersTabSet"

<h1>Cascading Values & Parameters - TabSet</h1>

<TabSet>
    <Tab Title="First tab">
        <h4>Greetings from the first tab!</h4>

        <label>
            <input type="checkbox" @bind="showThirdTab" />
            Toggle third tab
        </label>
    </Tab>
    <Tab Title="Second tab">
        <h4>The second tab says Hello World!</h4>
    </Tab>

    @if (showThirdTab)
    {
        <Tab Title="Third tab">
            <h4>Welcome to the disappearing third tab!</h4>
            <p>Toggle this tab from the first tab.</p>
        </Tab>
    }
</TabSet>

<h2>ITab.cs</h2>

<p>The <code>ITab</code> interface is implemented by tabs:</p>

<pre><code>@@using Microsoft.AspNetCore.Blazor;

namespace BlazorSample.UIInterfaces
{
    public interface ITab
    {
        RenderFragment ChildContent { get; }
    }
}</code></pre>

<h2>Component</h2>

<p>This component (<em>CascadingValuesParametersTabSet.razor</em>) uses the <code>TabSet</code> component, which contains several <code>Tab</code> components:</p>

<pre><code>&lt;TabSet&gt;
    &lt;Tab Title="First tab"&gt;
        &lt;h4&gt;Greetings from the first tab!&lt;/h4&gt;

        &lt;label&gt;
            &lt;input type="checkbox" @@bind="showThirdTab" /&gt;
            Toggle third tab
        &lt;/label&gt;
    &lt;/Tab&gt;
    &lt;Tab Title="Second tab"&gt;
        &lt;h4&gt;The second tab says Hello World!&lt;/h4&gt;
    &lt;/Tab&gt;

    @@if (showThirdTab)
    {
        &lt;Tab Title="Third tab"&gt;
            &lt;h4&gt;Welcome to the disappearing third tab!&lt;/h4&gt;
            &lt;p&gt;Toggle this tab from the first tab.&lt;/p&gt;
        &lt;/Tab&gt;
    }
&lt;/TabSet&gt;
    
@@code
{
    private bool showThirdTab;
}</code></pre>

<h2>TabSet.csthml</h2>

<p>The child <code>Tab</code> components aren't explicitly passed as parameters to the <code>TabSet</code>. Instead, the child <code>Tab</code> components are part of the child content of the <code>TabSet</code>. However, the <code>TabSet</code> still needs to know about each <code>Tab</code> so that it can render the headers and the active tab. To enable this coordination without requiring additional code, the <code>TabSet</code> component <em>can provide itself as a cascading value</em> that is then picked up by the descendent <code>Tab</code> components.</p>

<pre><code>@@using BlazorSample.UIInterfaces

&lt;!-- Display the tab headers --&gt;
&lt;CascadingValue Value=this&gt;
    &lt;ul class="nav nav-tabs"&gt;
        @@ChildContent
    &lt;/ul&gt;
&lt;/CascadingValue&gt;

&lt;!-- Display body for only the active tab --&gt;
&lt;div class="nav-tabs-body p-4"&gt;
    @@ActiveTab?.ChildContent
&lt;/div&gt;

@@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public ITab ActiveTab { get; private set; }

    public void AddTab(ITab tab)
    {
        if (ActiveTab == null)
        {
            SetActivateTab(tab);
        }
    }

    public void RemoveTab(ITab tab)
    {
        if (ActiveTab == tab)
        {
            SetActivateTab(null);
        }
    }

    public void SetActivateTab(ITab tab)
    {
        if (ActiveTab != tab)
        {
            ActiveTab = tab;
            StateHasChanged();
        }
    }
}</code></pre>

<h2>Tab.razor</h2>

<p>The descendent <code>Tab</code> components capture the containing <code>TabSet</code> as a cascading parameter, so the <code>Tab</code> components add themselves to the <code>TabSet</code> and coordinate on which <code>Tab</code> is active.</p>

<pre><code>@@using BlazorSample.UIInterfaces
@@implements ITab

&lt;li&gt;
    &lt;a @@onclick="Activate" class="nav-link @@TitleCssClass" role="button"&gt;
        @@Title
    &lt;/a&gt;
&lt;/li&gt;

@@code {
    [CascadingParameter]
    private TabSet ContainerTabSet { get; set; }

    [Parameter]
    public string Title { get; set; }

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    private string TitleCssClass => ContainerTabSet.ActiveTab == this ? "active" : null;

    protected override void OnInitialized()
    {
        ContainerTabSet.AddTab(this);
    }

    private void Activate()
    {
        ContainerTabSet.SetActivateTab(this);
    }
}</code></pre>

@code
{
    private bool showThirdTab;
}
